package algorithm.ACO_algo.ACS;

import algorithm.ACO_algo.Entity.*;
import algorithm.ACO_algo.util.*;
import algorithm.ACO_algo.output.*;

import java.text.ParseException;
import java.util.*;

//对一个卫星上的可执行任务序列进行求最优解
public class OneSat {
    public List<Job> getTasks() {
        return tasks;
    }

    public void setTasks(List<Job> tasks) {
        this.tasks = tasks;
    }

    public List<Job> tasks;
    public int antNum;
    public int IterMax;
    public List<Satellite> satellities;
    public ArrayList<Integer> have_job;
    public Integer sat_id;
    public ArrayList<Integer>[] untreated;//记录蚂蚁未安排的任务节点

    public ArrayList<Integer>[] getRoutes() {
        return routes;
    }

    public void setRoutes(ArrayList<Integer>[] routes) {
        this.routes = routes;
    }

    public ArrayList<Integer>[] routes;//记录蚂蚁安排的任务节点路径
    public double[][] pheromone; // 信息素
    public double[][] herustic; // 启发值
    public double[][] infoPhe; //计算任务节点转移概率 infoPhe = pheromone ^ alpha * herustic ^ beta
    public double pheromone_0; // 信息素初始值
    public int alpha, beta;
    public double p; // 信息素挥发率
    public double q_0;//决定状态转移如何计算的参数
    public Random rand;
    public int[] r;
    //public SatellityResult sat_result;//该卫星的任务安排解
    public SatellityResult[] sat_results;
    public SatellityResult best_sat_result;
    public int taskNum;
    public ArrayList<TwandJob> satresource;

    public OneSat(Integer sat_id,ArrayList<Integer> have_job, List<Job> tasks , List<Satellite> satellities , Parameter parameter) {
        this.tasks = tasks;
        this.sat_results = new SatellityResult[antNum];
        this.best_sat_result = new SatellityResult();
        this.satresource = new ArrayList<TwandJob>();
        this.sat_id = sat_id;
        this.taskNum = have_job.size();
        this.have_job = have_job;
        this.pheromone = new double[taskNum][taskNum];
        this.herustic = new double[taskNum][taskNum];
        this.infoPhe = new double[taskNum][taskNum];
        this.antNum = parameter.antNum;
        this.satellities = satellities;
        this.IterMax = parameter.IterMax;
        this.alpha = parameter.Alpha;
        this.beta = parameter.Beta;
        this.p = parameter.p;
        this.q_0 = parameter.q_0;
        this.rand = new Random();
        this.untreated = new ArrayList[antNum]; // 未访问的数组数量等于蚂蚁数
        this.routes = new ArrayList[antNum];
        for (int i = 0; i < antNum; i++) untreated[i] = new ArrayList<Integer>();
        this.r = new int[antNum];

    }

    public Job find_job_by_id(Integer id){
        Job t = new Job();
        for(int i = 0; i < tasks.size(); i++){
            if(tasks.get(i).getJob_Id().equals(id)){
                t = tasks.get(i);
            }
        }
        return t;
    }

    public Satellite find_sat_by_id(Integer id){
        Satellite sa = new Satellite();
        for(int i = 0; i< satellities.size(); i++){
            if(id.equals(satellities.get(i).getSat_id())){
                sa = satellities.get(i);
            }
        }
        return sa;
    }

    public void find_satres(){
        for(int i=0;i<have_job.size();i++){
            Job temp_job = find_job_by_id(have_job.get(i));
            List<TimeWindow> tws_temp = temp_job.getTimeWindows();
            for(int j=0;j<tws_temp.size();j++){
                if(tws_temp.get(j).getSat_id() == sat_id){
                    TwandJob taj_temp = new TwandJob();
                    taj_temp.setJob_id(have_job.get(i));
                    taj_temp.setTw(tws_temp.get(j));
                    satresource.add(taj_temp);
                }
            }
        }
    }

    public void init() {
        // 计算信息素初始值
        pheromone_0 = 3.0;
        sat_results = new SatellityResult[antNum];
        best_sat_result.setSat_id(sat_id);
        best_sat_result.setRoute(new ArrayList<Integer>());
        best_sat_result.setListres(new ArrayList<TwandJob>());
        for(int m=0;m<sat_results.length;m++) {
            sat_results[m] = new SatellityResult();
            sat_results[m].setSat_id(sat_id);
            sat_results[m].setRoute(new ArrayList<Integer>());
            sat_results[m].setListres(new ArrayList<TwandJob>());
        }

        // 初始化信息素、启发值
        for (int i = 0; i < have_job.size(); i++) {
            for (int j = 0; j < have_job.size(); j++) {
                if (i != j) {
                    pheromone[i][j] = pheromone_0;
                    infoPhe[i][j] = Math.pow(pheromone[i][j], alpha) * Math.pow(herustic[i][j], beta);
                }
            }
        }
        for (int i = 0; i < have_job.size(); i++) {
            for (int j = 0; j < have_job.size()-1; j++) {
                if(i!=j){
                    int pre_pri = find_job_by_id(have_job.get(i)).getPriority();
                    int post_pri = find_job_by_id(have_job.get(j)).getPriority();
                    if(pre_pri > post_pri){
                        herustic[i][j] = 2;
                    }
                    else{
                        herustic[i][j] = 1;
                    }
                }
            }
        }
    }

    // 初始化蚂蚁参数
        public void reset(){
        // 初始化蚂蚁的初始任务
        for (int i = 0; i < antNum; i++) {
            r[i] = have_job.get((int)(Math.random() * have_job.size()));//随机将m个蚂蚁置于n个任务上
        }
        // 初始化每个蚂蚁未安排的任务
        for (int i = 0; i < antNum; i++) {
            untreated[i].clear();
            for ( int j = 0; j < have_job.size(); j++) {
                if(have_job.get(j) != r[i]) {
                    untreated[i].add(have_job.get(j));
                }
            }
        }
    }

    // 构造完整解
    public void construct_solution() {
        // 为每一个蚂蚁分别构造解
        for (int i = 0; i < antNum; i++) {
            // 路径开始
            ArrayList<Integer> route_current = new ArrayList<Integer>();
            route_current.add(r[i]);//将初始任务插入route路径中
            while(untreated[i].size() != 0) {
                int next = select_next(i, r[i]);
                route_current.add(next);
                r[i] = next;
                for (int j = 0; j < untreated[i].size(); j++)
                    if (untreated[i].get(j) == next) untreated[i].remove(j);
                // 如果蚂蚁构建出一条完整的路径,则将该路径保存到routes数组中
            }
            routes[i] = route_current;
        }
    }

    public int getindex(int val){
        int index = -1;
        for(int i=0;i<have_job.size();i++){
            if(val == have_job.get(i)){
                index = i;
                break;
            }
        }
        return index;
    }

    //选择当前任务节点时的下一个任务节点
    public int select_next(int k, int current) {
        // 若全部处理完，返回配送中心
        // 计算概率

            double max_infoPhe = Integer.MIN_VALUE;
            int max_index = -1;
            for (int i = 0; i < untreated[k].size(); i++) {
                if(infoPhe[getindex(current)][getindex(untreated[k].get(i))] > max_infoPhe) {
                    max_infoPhe = infoPhe[getindex(current)][getindex(untreated[k].get(i))];
                    max_index = i;
                }
            }
            return untreated[k].get(max_index);

    }



    //得到该任务在某一窗口中的开始时间与结束时间
    public boolean is_allocated(Integer task_id, TimeWindow t) throws ParseException {
        boolean res = false;
        Job task = find_job_by_id(task_id);
        Date job_s = task.getStartT();
        Date tw_s = t.getStartT();
        Date job_e = task.getEndT();
        Date tw_e = t.getEndT();
        Date temp_s, temp_e;
        if (Timefunction.isLess(job_s, tw_s)) {
            temp_s = tw_s;
        } else {
            temp_s = job_s;
        }
        if (Timefunction.isLess(job_e, tw_e)) {
            temp_e = job_e;
        } else {
            temp_e = tw_e;
        }
        if(Timefunction.getSecondInterval(temp_e, temp_s) >= task.getDuration()){
            task.setStart(temp_s);
            task.setEnd(Timefunction.TimeCal(temp_s, task.getDuration()));
            res = true;
        }
        return res;
    }


    //判断某任务在该卫星中是否与其他任务冲突
    public boolean is_collision(Integer task_id, TimeWindow tw,SatellityResult sat_result) throws ParseException {
        boolean res = false;
        Job cur_ta = find_job_by_id(task_id);
        Satellite sat = find_sat_by_id(tw.getSat_id());

        //判断是否与该卫星的任务队列里的其他卫星存在时间冲突
        if(sat_result.getListres().size() == 0){
            return res;
        }
        for(int i =0; i < sat_result.getListres().size(); i++){
            Job temp = find_job_by_id(sat_result.getListres().get(i).getJob_id());
            Date temp_s, temp_e;

            if (Timefunction.isLess(cur_ta.getStart(), temp.getStart())) {
                temp_s = cur_ta.getStart();
            } else {
                temp_s = temp.getStart();
            }
            if (Timefunction.isLess(cur_ta.getEnd(), temp.getEnd())) {
                temp_e = temp.getEnd();
            } else {
                temp_e = cur_ta.getEnd();
            }
            if(Timefunction.getSecondInterval(temp_e, temp_s) < cur_ta.getDuration()+temp.getDuration()+sat.getMTI()){
                res = true;
                break;
            }
        }
        return res;
    }

    //在某一任务所有可能的窗口中按照最先开始时间选择窗口
    public TimeWindow selected(Integer job_id) throws ParseException {
        TimeWindow t = new TimeWindow();
        ArrayList<TimeWindow> tws_temp = new ArrayList<TimeWindow>();
        for(int i=0;i<satresource.size();i++){
            if(satresource.get(i).getJob_id() == job_id){
                tws_temp.add(satresource.get(i).getTw());
            }
        }
        for(int j=0;j<tws_temp.size();j++){
            if(is_allocated(job_id,tws_temp.get(j))){
                t = tws_temp.get(j);
                break;
            }
        }
        return t;
    }



    //下面是一条蚂蚁路径所生成的解，要解决任务冲突问题
    public SatellityResult generatesolutins(ArrayList<Integer> route ) throws ParseException {
        SatellityResult sat_result = new SatellityResult();
        sat_result.setSat_id(sat_id);
        sat_result.setListres(new ArrayList<TwandJob>());
        sat_result.setRoute(route);
        for(int i =0;i<route.size();i++){
            Job job_temp = find_job_by_id(route.get(i));
            TimeWindow tw_temp = selected(route.get(i));//有问题，怎么一个选项都没有
            if(tw_temp.getSat_id() != null){
                if(is_collision(route.get(i),tw_temp, sat_result) == false){
                    //setDate(route.get(i),tw_temp);
                    TwandJob temp_1 = new TwandJob();
                    job_temp.setUsed(true);
                    temp_1.setJob_id(route.get(i));
                    temp_1.setTw(tw_temp);
                    sat_result.getListres().add(temp_1);
                }
            }
        }
        return sat_result;
    }


    //接下来为每个蚂蚁路径上的任务安排资源并生成解
    public void resource_allocate() throws ParseException {
        for(int i = 0;i < routes.length;i++){
            sat_results[i] = generatesolutins(routes[i]);
        }
    }

    // 更新信息素,记录最优解
    public void update_pheromone() {
        SatellityResult now_best = new SatellityResult();
        double delta = 0;
        double max_reward = sat_results[0].getListres().size();
        int best_index = 0;
        // 查找最优解
        for (int i = 1; i < antNum; i++) {
            if (sat_results[i].getListres().size() > max_reward){
                max_reward = sat_results[i].getListres().size();
                best_index = i;
            }
        }
        now_best = sat_results[best_index];


        // 更新最优解 若当前最优代替历史最优，增加信息素时获得增益
        if (now_best.getListres().size() > best_sat_result.getListres().size()) {
            if(best_sat_result.getListres().size() != 0) {
                delta = (now_best.getListres().size() - best_sat_result.getListres().size()) / best_sat_result.getListres().size();
            }
            best_sat_result = now_best;
        }

        //更新信息素含量
        // 信息素挥发
        for (int i = 0; i < taskNum; i ++)
            for (int j = 0; j < taskNum; j ++)
                pheromone[i][j] *= (1 - p);
        // 信息素增加
        for (int i = 0; i < have_job.size()-1; i ++){
            pheromone[getindex(now_best.route.get(i))][getindex(now_best.route.get(i + 1))]
                    += (1 / (double)now_best.getListres().size()) * (1 + delta);
        }
    }


    //按照最大循环次数，开始迭代执行蚁群算法
    public SatellityResult ACS_Strategy() throws ParseException {
        init();
        for (int i = 0; i < IterMax; i++) {
            reset();//初始化agent信息
            find_satres();
            construct_solution();//对于所有的蚂蚁构造一个完整的route
            resource_allocate();//为每个路径分配资源，生成解
            update_pheromone();//更新信息素
        }
        return best_sat_result;
    }



}
